home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / OpenDoc 1.2b2c1 / Implementation / DocShell / ShellMem.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-13  |  16.2 KB  |  497 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ShellMem.cpp
  3.  
  4.     Contains:    Memory management for the Shell
  5.  
  6.     Owned by:    Nick Pilch
  7.  
  8.     Copyright:    © 1995 - 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.         <15>      1/9/97    TJ        Fixed decleration of size ptrs.
  13.         <14>      1/8/97    DH        1401434: Make out-of-temp mem non fatal
  14.                                     (user can cancel quit. Properly notify user
  15.                                     of different out-of-mem situations.
  16.         <13>      12/19/96    TJ        Fixed decleration of some size prtrs.
  17.         <12>      12/18/96    DH        1375780,1612023: Handled more cases of low
  18.                                     memory (especially Read Only docs) and
  19.                                     changed how we deal with others
  20.                                     (out-of-temp mem is no longer always
  21.                                     fatal).
  22.         <11>      10/22/96    DH        1336808    ,1333949,1368888,1375780 Radically
  23.                                     changed low mem handling. We now check for
  24.                                     low memory thresholds; warn the user when
  25.                                     space is low; allow them to adjust the doc
  26.                                     heap size if app space is low; close the
  27.                                     document if we run out of memory.  Low-mem
  28.                                     dialogs have also been completely
  29.                                     redesigned, and display accurate
  30.                                     information about the low-memory situation,
  31.                                     including recommendations to user on how to
  32.                                     deal with them.
  33.         <10>    27.09.1996    NP        1385775: Incorrect dialog after OD
  34.                                     initialization fails.
  35.          <9>     9/19/96    DH        Task: low memory changes. 1377922, 1377888.
  36.                                     Low-mem threshold change. Also, we now exit
  37.                                     when we are very low on memory.
  38.          <8>     7/30/96    DH        Changed unknown macro/function BREAK to
  39.                                     WARN.
  40.          <7>     7/30/96    eeh        1372943: add ODShellCorruptDocGoodbye and
  41.                                     ODShellGenericGoodbye
  42.          <6>     7/28/96    DH        Bug#1372954: Changed LowMemoryAlert to call
  43.                                     ExitToShell instead of ODCloseDocument.
  44.                                     This was done because that utility routine
  45.                                     can crash under low memory situations.
  46.          <5>     6/21/96    jpa        T10002: Lowered min app heap free space to
  47.                                     32k, 6k contig.
  48.          <4>    .04.1996    NP        1339832: Send exit event before closing
  49.                                     documents/windows from LowMemoryAlert.
  50.          <3>      4/4/96    NP        1338241: Unstaticize function.
  51.          <2>     3/29/96    DM        1334273: show low mem quit alert and then
  52.                                     exit to shell when unable to show another
  53.                                     low mem alert due to out-of-mem err
  54.          <1>    10/24/95    jpa        first checked in
  55.     
  56.     In Progress:
  57.         
  58. */
  59.  
  60.  
  61. #ifndef _RLSHELL_
  62. #include "RlShell.h"
  63. #endif
  64.  
  65. #ifndef _SHELLDEF_
  66. #include "ShellDef.h"
  67. #endif
  68.  
  69. #ifndef _DISPTCH_
  70. #include <Disptch.xh>
  71. #endif
  72.  
  73. #ifndef _MEMMGR_
  74. #include <MemMgr.h>
  75. #endif
  76.  
  77. #ifndef _DLOGUTIL_
  78. #include <DlogUtil.h>
  79. #endif
  80.  
  81. #ifndef _DOCUTILS_
  82. #include <DocUtils.h>
  83. #endif
  84.  
  85. #ifndef _USERSRCM_
  86. #include <UseRsrcM.h>
  87. #endif
  88.  
  89. #ifndef _TEMPITER_
  90. #include <TempIter.h>
  91. #endif
  92.  
  93. #ifndef _SHELLMEM_
  94. #include <ShellMem.h>
  95. #endif
  96.  
  97. #ifndef SOM_ODWindowState_xih
  98. #include <WinStat.xh>
  99. #endif
  100.  
  101. #ifndef __ICONS__
  102. #include <Icons.h>
  103. #endif
  104.  
  105. //==============================================================================
  106. // CONSTANTS
  107. //==============================================================================
  108.  
  109. const size_t kSlushFundSize            = 10 * 1024;    // Size of memory slush-fund
  110. const size_t kSlushFundAllocLimit    =  2 * 1024;    // Max request that will free slush fund
  111.  
  112. const ODSize kGoodAppFreeSpace         = 94 * 1024;    // Sizes below which we notify the user
  113. const ODSize kGoodAppContigSpace     = 32 * 1024;
  114. const ODSize kGoodTempFreeSpace     = 128 * 1024;
  115. const ODSize kGoodTempContigSpace     = 64 * 1024;
  116. const ODSize kBailAppFreeSpace        = 2 * 1024;    // App-heap mem requirement of low-mem alert
  117.  
  118. // This should be moved to ShellDef.h.
  119. const short     cancelButtonID            = 2;
  120.  
  121. //==============================================================================
  122. // FUNCTION DEFINITIONS
  123. //==============================================================================
  124.  
  125. static ODBoolean    DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document );
  126.  
  127. //==============================================================================
  128. // METHODS
  129. //==============================================================================
  130.  
  131. //-------------------------------------------------------------------------------------
  132. // InitMemory
  133. //-------------------------------------------------------------------------------------
  134.  
  135. void
  136. RealShell::InitMemory( )
  137. {
  138.     // Preload low-mem alerts so we'll be able to use them when space is low:
  139.     CUsingLibraryResources r;
  140.     ::Get1Resource('ALRT',kSHLphTempMemIsLow);
  141.     ::Get1Resource('DITL',kSHLphTempMemIsLow);
  142.     ::Get1Resource('DLOG',kSHLphTempMemIsLowFatal);
  143.     ::Get1Resource('DITL',kSHLphTempMemIsLowFatal);
  144.     ::Get1Resource('ALRT',kSHLphAppSpaceIsLow);
  145.     ::Get1Resource('DITL',kSHLphAppSpaceIsLow);    
  146.     ::Get1Resource('ALRT',kSHLphAppSpaceIsLowRO);
  147.     ::Get1Resource('DITL',kSHLphAppSpaceIsLowRO);
  148.     ::Get1Resource('DLOG',kSHLphAppSpaceIsLowDlg);
  149.     ::Get1Resource('DITL',kSHLphAppSpaceIsLowDlg);
  150.     
  151.     if( ! MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit) )
  152.         WARN("Could not allocate slush fund");
  153. }
  154.  
  155.  
  156. //-------------------------------------------------------------------------------------
  157. // Purge
  158. //-------------------------------------------------------------------------------------
  159.  
  160. ODSize    RealShell::Purge(ODSize size)
  161. {
  162.     ODSize result;
  163.     TRY{
  164.         result= fSession->Purge(fEV,size);
  165.     }CATCH_ALL{
  166.         result = 0;
  167.         // do not reraise, ignore exception
  168.     }ENDTRY
  169.     return result;
  170. }
  171.  
  172.  
  173. //-------------------------------------------------------------------------------------
  174. // IsFreeMemoryLow
  175. //-------------------------------------------------------------------------------------
  176.  
  177. inline ODSLong Max( ODSLong a, ODSLong b )
  178. {return a>b ?a :b;}
  179.     
  180. ODSize RealShell::IsFreeMemoryLow( ODBoolean &appIsLow, ODBoolean &tempIsLow )
  181. {
  182.     // First check/replenish the memory slush fund:
  183.     ODBoolean slushy = kODFalse;
  184.     if( MMSlushFundSize(kDefaultHeapID) > 0 )
  185.         slushy = kODTrue;
  186.     else
  187.         slushy = MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit);
  188.     
  189.     size_t totalFree = 0,contig = 0;
  190.     ODSLong freeDelta = 0, contigDelta = 0;
  191.     ODSLong purgeApp = 0, purgeTemp = 0;
  192.     
  193.     MMSystemFreeSpace(kMMAppMemory, &totalFree,&contig);
  194.  
  195.     freeDelta = kGoodAppFreeSpace-totalFree;
  196.     contigDelta = kGoodAppContigSpace-contig;
  197.     purgeApp = Max(freeDelta,contigDelta);
  198. //    appIsLow = (purgeApp>0); 
  199.     if( totalFree<kGoodAppFreeSpace || contig<kGoodAppContigSpace )
  200.         appIsLow = 1;
  201.     
  202.     MMSystemFreeSpace(kMMTempMemory, &totalFree,&contig);
  203.     freeDelta = kGoodTempFreeSpace-totalFree;
  204.     contigDelta = kGoodTempContigSpace-contig;
  205.     purgeTemp = Max(freeDelta,contigDelta);
  206. //    tempIsLow = (purgeTemp>0);    
  207.     if( totalFree<kGoodTempFreeSpace || contig<kGoodTempContigSpace )
  208.         tempIsLow = 1;
  209.  
  210.     if( !slushy )
  211.         tempIsLow = kODTrue;
  212.     
  213.     purgeApp = Max(purgeApp,purgeTemp);
  214.     return (purgeApp>0 ?purgeApp :0);
  215. }
  216.  
  217.  
  218. //-------------------------------------------------------------------------------------
  219. // CheckFreeMemory
  220. //-------------------------------------------------------------------------------------
  221.  
  222. ODBoolean RealShell::CheckFreeMemory( )
  223. {
  224.     // If space is low, purge. If space is still low, alert the user, provided this
  225.     // process is active. After the user is alerted, set a flag so we don't put up
  226.     // an endless stream of alerts. Note that we don't use the normal dialog filter,
  227.     // since it calls the Dispatcher, which may cause trouble when space is low.
  228.     
  229.     CUsingLibraryResources rez;
  230.     
  231.     ODBoolean appIsLow = kODFalse, tempIsLow = kODFalse;
  232.     ODSize purge = this->IsFreeMemoryLow(appIsLow,tempIsLow);
  233.     
  234.     if( purge ) {
  235.         // Low on free space, so purge:
  236.         this->Purge( 2*purge );
  237.         
  238.         this->IsFreeMemoryLow(appIsLow,tempIsLow);
  239.         
  240.         if( (appIsLow || tempIsLow) ) {
  241.             // Yow, still low on memory after purging.
  242.             size_t freeApp=0,contigApp=0, freeTemp=0,contigTemp = 0;
  243.             MMSystemFreeSpace(kMMAppMemory, &freeApp,&contigApp);
  244.             MMSystemFreeSpace(kMMTempMemory, &freeTemp,&contigTemp);
  245.             
  246.             // If app heap memory gets this low, OpenDoc cannot reliably function. Sorry, but
  247.             // we must exit the scene.
  248.             if( freeApp<kGoodAppFreeSpace/2 || contigApp<kGoodAppContigSpace/2 )
  249.             {
  250.                 // But first, allow the user to increase the heapsize of the document so they
  251.                 // will have more memory next time to work with that document.
  252.                 this->LowAppMemoryAlert( kODTrue );    // Fatal low-mem alert.
  253.                 ::ExitToShell(); // Yikes!! Does this ever blow!
  254.             }
  255.             // Likewise with temp mem, we must quit if temp mem space gets too low. However, we
  256.             // will allow the user to cancel this quitting and switch to the Finder to free up memory.
  257.             if(    (freeTemp<kGoodTempFreeSpace/2) || (contigTemp<kGoodTempContigSpace/2) )
  258.             {
  259.                 if( !fOutOfTempMemNotified )
  260.                 {
  261.                     ODShellLowTempMemoryGoodbye();
  262.                     fOutOfTempMemNotified = kODTrue; // Only notify user 1st time. Otherwise
  263.                                                      // they get an endless sequence of dialogs.
  264.                 }
  265.                 return kODFalse;
  266.             }
  267.             // Otherwise, if temp-mem is low, let the user know that closing docs/apps
  268.             // can free up memory to allow the document to be reopened safely.
  269.             else
  270.             {
  271.                 // We are in a low memory state here, so notify user of the particular condition,
  272.                 // including options to change the memory size, save or close the document.
  273.                 if( fProcessIsActive && ( freeApp > kBailAppFreeSpace ) )
  274.                 {    
  275.                     if( ((freeApp < kGoodAppFreeSpace) || (contigApp < kGoodAppContigSpace))
  276.                         && !fLowAppMemNotified )
  277.                     {
  278.                         this->LowAppMemoryAlert( kODFalse );
  279.                         fLowAppMemNotified = kODTrue;    // Only notify user 1st time. Otherwise
  280.                     }                                    // they get an endless sequence of dialogs.
  281.                     if( ((freeTemp < kGoodTempFreeSpace) || (contigTemp < kGoodTempContigSpace))
  282.                         && !fLowTempMemNotified )
  283.                     {
  284.                         this->LowTempMemoryAlert( );
  285.                         fLowTempMemNotified = kODTrue;    // Only notify user 1st time. Otherwise
  286.                     }                                    // they get an endless sequence of dialogs.
  287.                 }
  288.                 return kODFalse;
  289.             }
  290.         }
  291.     }
  292.     fLowTempMemNotified = kODFalse;        // We're okay now, clear notification state
  293.     fLowAppMemNotified = kODFalse;
  294.     fOutOfTempMemNotified = kODFalse;
  295.  
  296.     return kODTrue;
  297. }
  298.  
  299.  
  300. static ODBoolean
  301. DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document )
  302. {
  303.     // Throws no exceptions.
  304.     TRY{
  305.         return ODDocumentHasChanges(ev,session,document);
  306.     }CATCH_ALL{
  307.         WARN("ODDocumentHasChanges failed, err %d",ErrorCode());
  308.     }ENDTRY
  309.     return kODFalse;
  310. }
  311.  
  312. #if ODDebug
  313. void BREAK( const char[] );
  314. #endif
  315.  
  316. static void GoodbyeAlert( short alertID )
  317. {
  318. #if ODDebug
  319.     WARN("about to show fatal low memory or related alert...");
  320. #endif
  321.     GenericAlert(alertID);
  322.     // make sure CUsingLibraryResources goes out of scope first...
  323.     ::ExitToShell(); // bye bye process
  324. }
  325.  
  326. void ODShellLowTempMemoryGoodbye()
  327. {
  328. //    GoodbyeAlert( kSHLphTempMemIsLowFatal );
  329. #if ODDebug
  330.     WARN("about to show fatal low memory or related alert...");
  331. #endif
  332.  
  333.     short idClicked = StopAlert( kSHLphTempMemIsLowFatal,kODNULL);
  334.     if( idClicked != cancelButtonID )
  335.         ::ExitToShell(); // bye bye process
  336. }
  337.  
  338. void ODShellGenericGoodbye( ODBoolean inform )
  339. {
  340.     if ( inform )
  341.         GoodbyeAlert( kODAlertShellGenericError );
  342.     else
  343.         ::ExitToShell();
  344. }
  345.  
  346. void ODShellCorruptDocGoodbye()
  347. {
  348.     GoodbyeAlert( kODAlertShellCorruptDocError );
  349. }
  350.  
  351. //------------------------------------------------------------------------------
  352. // DispatchExitEvent
  353. //
  354. //    Duplicated in ODSessnB.cpp
  355. //------------------------------------------------------------------------------
  356.  
  357. static void    DispatchExitEvent(Environment* ev, ODDispatcher* dispatcher)
  358. {
  359. // Copied from RealShell::FakePrintMenuEvent
  360.     ODEventData event;
  361.     event.message = 0L;
  362.     event.what = kODEvtExit;
  363.     // zero the rest of the fields
  364.     WASSERT( sizeof(Point) == sizeof(long) );
  365.     *(long*)&event.where = 0L;
  366.     event.when = 0L;
  367.     event.modifiers = 0;
  368.     dispatcher->Dispatch(ev, &event);
  369. }
  370.  
  371. //------------------------------------------------------------------------------
  372. // RealShell::LowTempMemoryAlert
  373. //------------------------------------------------------------------------------
  374.  
  375. void RealShell::LowTempMemoryAlert( void )
  376. {
  377.     /*    Do not use the regular ODDialogFilter for this alert, as it calls back to the
  378.         Dispatcher, which calls other OpenDoc routines. This could be dangerous/fatal
  379.         in precisely this kind of low memory situation. */
  380.  
  381.     ODSShort result; ODVolatile(result);
  382.     TRY{
  383.         // Shazam! Show the alert:
  384.  
  385.         // $$$$$ dh -  Should use ShowAlert instead.
  386.         CautionAlert( kSHLphTempMemIsLow, kODNULL );
  387. //        ShowAlert( GetGlobalEnvironment(),kSHLphTempMemIsLow,kODNULL,fSession );
  388.      }CATCH_ALL{
  389.         if ( ErrorCode() == kODErrOutOfMemory )
  390.         {
  391.             ODShellLowTempMemoryGoodbye(); // goodbye cruel world
  392.         }
  393.         else
  394.         {
  395.             WARN("Error %d showing low mem alert",ErrorCode());
  396.             // don't reraise
  397.         }
  398.     }ENDTRY
  399. }
  400.  
  401. //-------------------------------------------------------------------------------------
  402. // RealShell::LowAppMemoryAlert
  403. //
  404. // Displays dialog informing the user that the app heap is low on memory, and that 
  405. // continuing could lead to data loss. It then allows the user to adjust the size of
  406. // the document, and save and close. The user can also cancel and do other cleanup
  407. // before quitting.
  408. //-------------------------------------------------------------------------------------
  409.  
  410. ODBoolean RealShell::LowAppMemoryAlert( ODBoolean fatalLowMem )
  411. {
  412. /*
  413.     There's a lot to do in this routine. We want to display a dialog informing the user
  414.     that the app heap is running low, then allow them to change the doc size, and close
  415.     the file. Here's how it goes:
  416.         • Check if the document is read-only or not. If it is read-only, then we should
  417.         display a dialog merely informing the user that memory is low without any 
  418.         controls to change the memory size.
  419.         • Check if the document needs to be saved. If so, inform the user that the
  420.         document will be saved if they decide to change the memory size.
  421.         • Present the user with controls to adjust the memory size.
  422.         • Inform the user that choosing OK will set the size of the doc to the new amount,
  423.         possibly save, then close and reopen the document. Then do it!
  424. */
  425.     ODBoolean needSave = kODFalse; ODVolatile(needSave);
  426.     ODFacet* sizeFacet = kODNULL; ODVolatile(sizeFacet);
  427.     ODBoolean hasWriteAccess = kODFalse; ODVolatile(hasWriteAccess);
  428.     ODDraft*  docDraft = kODNULL; ODVolatile(sizeFacet);
  429.     PlatformFile* docFile = kODNULL; ODVolatile(docFile);
  430.     short buttonHit = 0;
  431.     ODBoolean changeMade = kODFalse; ODVolatile(changeMade);
  432.     
  433.     WASSERT(fSession != kODNULL);
  434.     if (!fSession)
  435.         THROW(kODErrIllegalNullInput);
  436.  
  437. //    ArrowCursor();
  438.  
  439.     // Check if document is read-only. If so, display alert without controls and
  440.     // text that informs user that document should/needs to be closed.
  441.     TRY
  442.         TempODWindow window = fSession->GetWindowState(fEV)->AcquireFrontRootWindow(fEV);
  443.         sizeFacet = window->GetRootFacet(fEV);
  444.         docDraft  = ODGetDraftOfWindow(fEV, window);
  445.         hasWriteAccess = ODDraftHasWriteAccess(fEV, docDraft);
  446.     CATCH_ALL
  447.         if ( ErrorCode() == kODErrOutOfMemory )
  448.             ODShellLowTempMemoryGoodbye(); // goodbye cruel world
  449.         else
  450.         {    // Since we couldn't successfully check if the doc is read-only,
  451.             // we will pessimistically assume we do not have write access.
  452.             hasWriteAccess = kODFalse;
  453.             WARN("Couldn't check doc permissions because error %d.",ErrorCode());
  454.             // don't reraise, just go on.
  455.         }
  456.     ENDTRY
  457.  
  458.     CUsingLibraryResources duh;
  459.  
  460.     if( !hasWriteAccess )
  461.     {
  462.         if( fatalLowMem )
  463.             CautionAlert( kSHLphAppSpaceIsLowFatalRO, kODNULL);
  464.         else
  465.             CautionAlert( kSHLphAppSpaceIsLowRO, kODNULL);
  466.     }
  467.     else
  468.     {    
  469.         // Even if we have a fatal low mem situation, allow the user to change the doc mem size.
  470.         ODError reopenErr = noErr;
  471.         
  472.         TRY
  473.             docFile = GetPlatformFileFromContainer(fEV, docDraft->GetDocument(fEV)->GetContainer(fEV));
  474.             FSSpec    docFSSpec = docFile->GetFileSpec();
  475.             ODBoolean changeMade = ChangeDocSize(&docFSSpec, kODTrue, fatalLowMem);    // Increase Size
  476.             if( changeMade == kODTrue && fatalLowMem != kODTrue )                         
  477.                 reopenErr = CloseAndReopenDoc(&docFSSpec);
  478.         CATCH_ALL
  479.             // If we ran out of memory trying to do this, then OpenDoc aint getting any better soon!
  480.             // $$$$$ - should we actually quit here? This routine could potentially use a lot
  481.             // of memory, and I'm not sure it's a good idea to close the document
  482. //                if ( ErrorCode() == kODErrOutOfMemory )
  483. //                    ODShellLowMemoryGoodbye(); // goodbye cruel world
  484.             // Since we don't know if we changed the size of the doc, pessimistically
  485.             // return that we couldn't.
  486.             changeMade = kODFalse;
  487.             WARN("Couldn't change the size of the doc because error %d.",ErrorCode());
  488.             // don't reraise, just go on.
  489.         ENDTRY
  490. //        }
  491.     }
  492.     if( fatalLowMem)
  493.         ::ExitToShell();    // Now we must die.
  494.         
  495.     return changeMade;
  496. }
  497.